#include <iostream>
#include <string>
#include <fstream>  
#include <streambuf> 
using namespace std;

#include <modp_b64.h>

std::string Base64Encode(std::string& input) {
    std::string temp;
    temp.resize(modp_b64_encode_len(input.size()));  // makes room for null byte

    // modp_b64_encode_len() returns at least 1, so temp[0] is safe to use.
    size_t output_size = modp_b64_encode(&(temp[0]), input.data(), input.size());

    temp.resize(output_size);  // strips off null byte

    return std::move(temp);
}

std::string Base64Decode(std::string& input) {
    std::string temp;
    temp.resize(modp_b64_decode_len(input.size()));

    // does not null terminate result since result is binary data!
    size_t input_size = input.size();
    size_t output_size = modp_b64_decode(&(temp[0]), input.data(), input_size);
    if (output_size == MODP_B64_ERROR)
        return std::string();

    temp.resize(output_size);

    return std::move(temp);
}

std::string ReadFile(std::string& input) { 
    std::ifstream t(input);  
    std::string str((std::istreambuf_iterator<char>(t)),  
                 std::istreambuf_iterator<char>());
    return std::move(str);
}

bool WriteFile(std::string& input, std::string& con) {
    ofstream file;
    file.open(input, std::ios::out);
    if (!file.is_open())
        return false;
    file << con;
    file.close();
    return true;
}

void show() {
    cout << "--------------------------------" << endl;
    cout << "Welcome to use base64 to encode and decode!" << endl;
    cout << "       Copyright 2017 Dzlua.All right reserved." << endl;
    cout << "    Usage: base64 -e(--encode)/-d(--decode) infile [outfile]" << endl;
    cout << "        -e/--encode:  encode the give file" << endl;
    cout << "        -d/--decode:  decode the give file" << endl;
    cout << "        infile:  the file name to encode or decode." << endl;
    cout << "        outfile:  the file name to save. default: infile.dat" << endl;
    cout << "    Sample:" << endl;
    cout << "        base64 -e(--encode) app.js app.dat" << endl;
    cout << "        base64 -d(--decode) app.dat app.js" << endl;
    cout << endl;
}

int main(int argc, char** argv)
{
    show();

    std::string doe, inf, outf;
    if (argc > 1)
        doe = argv[1];
    if (argc > 2)
        inf = argv[2];
    if (argc > 3)
        outf = argv[3];
    if (doe.empty() || inf.empty()) {
        cout << "args error!" << endl;
        return 0;
    }

    if (outf.empty())
        outf = inf + ".out";

    bool bEncode = false;
    bool bRet = false;
    bool hascmd = false;

    if (doe == "--encode" || doe == "-e") {
        bEncode = true;
        hascmd = true;
    } else if (doe == "--decode" || doe == "-d") {
        bEncode = false;
        hascmd = true;
    }

    if (!hascmd) {
        cout << "error no cmd!" << endl;
        return 1;
    }
    
    cout << "-> base64 " << doe << " " << inf << " " << outf << endl;

    if (bEncode) {
        cout << "encode ..." << endl;
        std::string infile = ReadFile(inf);
        infile = Base64Encode(infile);
        bRet = WriteFile(outf, infile);
    } else {
        cout << "decode ..." << endl;
        std::string infile = ReadFile(inf);
        infile = Base64Decode(infile);
        bRet = WriteFile(outf, infile);
    }

    if (bRet) {
        cout << "done!" << endl;
    } else {
        cout << "failed!" << endl;
        return 2;
    }

    return 0;
}
